From 8648d5409ea53ef4bb6ad9b7940c32d663af41e5 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 13 Dec 2017 15:03:53 +0100 Subject: [PATCH] dnd: Pass content to gdk_drag_begin() Instead of just passing the GdkContentFormats, we are now passing the GdkContentProvider to gdk_drag_begin(). This means that GDK itself can now query the data from the provider directly instead of having to send selection events. Use this to provide the private API gdk_drag_context_write() that allows backends to pass an output stream that this data will be written to. Implement this as the mechanism for providing drag data on Wayland. And to make this all work, implement a content provider named GtkDragContent that is implemented by reverting to the old DND drag-data-get machinery inside GTK, so for widgets everything works just like before. --- gdk/broadway/gdkdnd-broadway.c | 13 ++- gdk/broadway/gdkprivate-broadway.h | 12 +- gdk/gdkdnd.c | 173 +++++++++++++++++++++++++++++ gdk/gdkdnd.h | 2 +- gdk/gdkdndprivate.h | 11 ++ gdk/gdkwindow.c | 23 ++-- gdk/gdkwindowimpl.h | 2 +- gdk/wayland/gdkdnd-wayland.c | 14 +-- gdk/wayland/gdkprivate-wayland.h | 2 +- gdk/wayland/gdkselection-wayland.c | 120 ++++++-------------- gdk/x11/gdkdnd-x11.c | 14 +-- gdk/x11/gdkprivate-x11.h | 12 +- gtk/gtkdnd.c | 156 +++++++++++++++++++++++++- 13 files changed, 422 insertions(+), 132 deletions(-) diff --git a/gdk/broadway/gdkdnd-broadway.c b/gdk/broadway/gdkdnd-broadway.c index c670d8d3d9..3747ca1e88 100644 --- a/gdk/broadway/gdkdnd-broadway.c +++ b/gdk/broadway/gdkdnd-broadway.c @@ -84,12 +84,12 @@ gdk_broadway_drag_context_finalize (GObject *object) /* Drag Contexts */ GdkDragContext * -_gdk_broadway_window_drag_begin (GdkWindow *window, - GdkDevice *device, - GdkContentFormats *formats, - GdkDragAction actions, - gint dx, - gint dy) +_gdk_broadway_window_drag_begin (GdkWindow *window, + GdkDevice *device, + GdkContentProvider *content, + GdkDragAction actions, + gint dx, + gint dy) { GdkDragContext *new_context; @@ -98,6 +98,7 @@ _gdk_broadway_window_drag_begin (GdkWindow *window, new_context = g_object_new (GDK_TYPE_BROADWAY_DRAG_CONTEXT, "display", gdk_window_get_display (window), + "content", content, NULL); return new_context; diff --git a/gdk/broadway/gdkprivate-broadway.h b/gdk/broadway/gdkprivate-broadway.h index bc40ea9c32..39523a9079 100644 --- a/gdk/broadway/gdkprivate-broadway.h +++ b/gdk/broadway/gdkprivate-broadway.h @@ -47,12 +47,12 @@ void gdk_broadway_window_set_nodes (GdkWindow *window, GPtrArray *node_textures); void _gdk_broadway_window_register_dnd (GdkWindow *window); -GdkDragContext * _gdk_broadway_window_drag_begin (GdkWindow *window, - GdkDevice *device, - GdkContentFormats *formats, - GdkDragAction actions, - gint dx, - gint dy); +GdkDragContext * _gdk_broadway_window_drag_begin (GdkWindow *window, + GdkDevice *device, + GdkContentProvider *content, + GdkDragAction actions, + gint dx, + gint dy); void _gdk_broadway_window_translate (GdkWindow *window, cairo_region_t *area, gint dx, diff --git a/gdk/gdkdnd.c b/gdk/gdkdnd.c index 956be1a196..2344b7ee65 100644 --- a/gdk/gdkdnd.c +++ b/gdk/gdkdnd.c @@ -29,6 +29,8 @@ #include "gdkwindow.h" #include "gdkintl.h" #include "gdkcontentformats.h" +#include "gdkcontentprovider.h" +#include "gdkcontentserializer.h" #include "gdkcursor.h" #include "gdkenumtypes.h" #include "gdkeventsprivate.h" @@ -48,7 +50,9 @@ static struct { enum { PROP_0, + PROP_CONTENT, PROP_DISPLAY, + PROP_FORMATS, N_PROPERTIES }; @@ -261,6 +265,12 @@ gdk_drag_context_set_property (GObject *gobject, switch (prop_id) { + case PROP_CONTENT: + context->content = g_value_dup_object (value); + if (context->content) + context->formats = gdk_content_provider_ref_formats (context->content); + break; + case PROP_DISPLAY: context->display = g_value_get_object (value); g_assert (context->display != NULL); @@ -282,10 +292,18 @@ gdk_drag_context_get_property (GObject *gobject, switch (prop_id) { + case PROP_CONTENT: + g_value_set_object (value, context->content); + break; + case PROP_DISPLAY: g_value_set_object (value, context->display); break; + case PROP_FORMATS: + g_value_set_boxed (value, context->formats); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -298,6 +316,8 @@ gdk_drag_context_finalize (GObject *object) GdkDragContext *context = GDK_DRAG_CONTEXT (object); contexts = g_list_remove (contexts, context); + + g_clear_object (&context->content); g_clear_pointer (&context->formats, gdk_content_formats_unref); if (context->source_window) @@ -352,6 +372,24 @@ gdk_drag_context_class_init (GdkDragContextClass *klass) object_class->set_property = gdk_drag_context_set_property; object_class->finalize = gdk_drag_context_finalize; + /** + * GdkDragContext:content: + * + * The #GdkContentProvider or %NULL if the context is not a source-side + * context. + * + * Since: 3.94 + */ + properties[PROP_CONTENT] = + g_param_spec_object ("content", + "Content", + "The content being dragged", + GDK_TYPE_CONTENT_PROVIDER, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS | + G_PARAM_EXPLICIT_NOTIFY); + /** * GdkDragContext:display: * @@ -369,6 +407,22 @@ gdk_drag_context_class_init (GdkDragContextClass *klass) G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + /** + * GdkDragContext:formats: + * + * The possible formats that the context can provide its data in. + * + * Since: 3.94 + */ + properties[PROP_FORMATS] = + g_param_spec_boxed ("formats", + "Formats", + "The possible formats for data", + GDK_TYPE_CONTENT_FORMATS, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS | + G_PARAM_EXPLICIT_NOTIFY); + /** * GdkDragContext::cancel: * @context: The object on which the signal is emitted @@ -655,6 +709,125 @@ gdk_drag_get_selection (GdkDragContext *context) return GDK_DRAG_CONTEXT_GET_CLASS (context)->get_selection (context); } +static void +gdk_drag_context_write_done (GObject *content, + GAsyncResult *result, + gpointer task) +{ + GError *error = NULL; + + if (gdk_content_provider_write_mime_type_finish (GDK_CONTENT_PROVIDER (content), result, &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, error); + + g_object_unref (task); +} + +static void +gdk_drag_context_write_serialize_done (GObject *content, + GAsyncResult *result, + gpointer task) +{ + GError *error = NULL; + + if (gdk_content_serialize_finish (result, &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, error); + + g_object_unref (task); +} + +void +gdk_drag_context_write_async (GdkDragContext *context, + const char *mime_type, + GOutputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GdkContentFormats *formats, *mime_formats; + GTask *task; + GType gtype; + + g_return_if_fail (GDK_IS_DRAG_CONTEXT (context)); + g_return_if_fail (context->content); + g_return_if_fail (mime_type != NULL); + g_return_if_fail (mime_type == g_intern_string (mime_type)); + g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (callback != NULL); + + task = g_task_new (context, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + g_task_set_source_tag (task, gdk_drag_context_write_async); + + formats = gdk_content_provider_ref_formats (context->content); + if (gdk_content_formats_contain_mime_type (formats, mime_type)) + { + gdk_content_provider_write_mime_type_async (context->content, + mime_type, + stream, + io_priority, + cancellable, + gdk_drag_context_write_done, + task); + gdk_content_formats_unref (formats); + return; + } + + mime_formats = gdk_content_formats_new ((const gchar *[2]) { mime_type, NULL }, 1); + mime_formats = gdk_content_formats_union_serialize_gtypes (mime_formats); + gtype = gdk_content_formats_match_gtype (formats, mime_formats); + if (gtype != G_TYPE_INVALID) + { + GValue value = G_VALUE_INIT; + GError *error = NULL; + + g_assert (gtype != G_TYPE_INVALID); + + g_value_init (&value, gtype); + if (gdk_content_provider_get_value (context->content, &value, &error)) + { + gdk_content_serialize_async (stream, + mime_type, + &value, + io_priority, + cancellable, + gdk_drag_context_write_serialize_done, + g_object_ref (task)); + } + else + { + g_task_return_error (task, error); + } + + g_value_unset (&value); + } + else + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("No compatible formats to transfer clipboard contents.")); + } + + gdk_content_formats_unref (mime_formats); + gdk_content_formats_unref (formats); + g_object_unref (task); +} + +gboolean +gdk_drag_context_write_finish (GdkDragContext *context, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, context), FALSE); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_drag_context_write_async, FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + void gdk_drop_read_async (GdkDragContext *context, const char **mime_types, diff --git a/gdk/gdkdnd.h b/gdk/gdkdnd.h index 4a3926e589..8f75a7d1d2 100644 --- a/gdk/gdkdnd.h +++ b/gdk/gdkdnd.h @@ -136,7 +136,7 @@ GInputStream * gdk_drop_read_finish (GdkDragContext * GDK_AVAILABLE_IN_ALL GdkDragContext * gdk_drag_begin (GdkWindow *window, GdkDevice *device, - GdkContentFormats *formats, + GdkContentProvider *content, GdkDragAction actions, gint dx, gint dy); diff --git a/gdk/gdkdndprivate.h b/gdk/gdkdndprivate.h index 5cb477acb6..127b514265 100644 --- a/gdk/gdkdndprivate.h +++ b/gdk/gdkdndprivate.h @@ -134,6 +134,7 @@ struct _GdkDragContext { GdkWindow *dest_window; GdkWindow *drag_window; + GdkContentProvider *content; GdkContentFormats *formats; GdkDragAction actions; GdkDragAction suggested_action; @@ -177,6 +178,16 @@ void gdk_drag_find_window (GdkDragContext *context, GdkWindow **dest_window, GdkDragProtocol *protocol); +void gdk_drag_context_write_async (GdkDragContext *context, + const char *mime_type, + GOutputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean gdk_drag_context_write_finish (GdkDragContext *context, + GAsyncResult *result, + GError **error); G_END_DECLS diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index c504bc889b..a9a15c7eec 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -6927,7 +6927,7 @@ gdk_window_register_dnd (GdkWindow *window) * gdk_drag_begin: * @window: the source window for this drag * @device: the device that controls this drag - * @formats: (transfer none): the offered formats + * @content: (transfer none): the offered content * @actions: the actions supported by this drag * @dx: the x offset to @device's position where the drag nominally started * @dy: the y offset to @device's position where the drag nominally started @@ -6940,14 +6940,19 @@ gdk_window_register_dnd (GdkWindow *window) * %NULL on error. */ GdkDragContext * -gdk_drag_begin (GdkWindow *window, - GdkDevice *device, - GdkContentFormats *formats, - GdkDragAction actions, - gint dx, - gint dy) -{ - return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->drag_begin (window, device, formats, actions, dx, dy); +gdk_drag_begin (GdkWindow *window, + GdkDevice *device, + GdkContentProvider *content, + GdkDragAction actions, + gint dx, + gint dy) +{ + g_return_val_if_fail (GDK_IS_WINDOW (window), NULL); + g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); + g_return_val_if_fail (gdk_window_get_display (window) == gdk_device_get_display (device), NULL); + g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (content), NULL); + + return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->drag_begin (window, device, content, actions, dx, dy); } /** diff --git a/gdk/gdkwindowimpl.h b/gdk/gdkwindowimpl.h index f1abe35307..4383b84538 100644 --- a/gdk/gdkwindowimpl.h +++ b/gdk/gdkwindowimpl.h @@ -219,7 +219,7 @@ struct _GdkWindowImplClass void (* register_dnd) (GdkWindow *window); GdkDragContext * (*drag_begin) (GdkWindow *window, GdkDevice *device, - GdkContentFormats *formats, + GdkContentProvider*content, GdkDragAction actions, gint dx, gint dy); diff --git a/gdk/wayland/gdkdnd-wayland.c b/gdk/wayland/gdkdnd-wayland.c index 208b71e20d..ac849741a9 100644 --- a/gdk/wayland/gdkdnd-wayland.c +++ b/gdk/wayland/gdkdnd-wayland.c @@ -548,12 +548,12 @@ create_dnd_window (GdkDisplay *display) } GdkDragContext * -_gdk_wayland_window_drag_begin (GdkWindow *window, - GdkDevice *device, - GdkContentFormats *formats, - GdkDragAction actions, - gint dx, - gint dy) +_gdk_wayland_window_drag_begin (GdkWindow *window, + GdkDevice *device, + GdkContentProvider *content, + GdkDragAction actions, + gint dx, + gint dy) { GdkWaylandDragContext *context_wayland; GdkDragContext *context; @@ -566,11 +566,11 @@ _gdk_wayland_window_drag_begin (GdkWindow *window, context_wayland = g_object_new (GDK_TYPE_WAYLAND_DRAG_CONTEXT, "display", display_wayland, + "content", content, NULL); context = GDK_DRAG_CONTEXT (context_wayland); context->source_window = g_object_ref (window); context->is_source = TRUE; - context->formats = gdk_content_formats_ref (formats); gdk_drag_context_set_device (context, device); diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h index 6b6e356a11..a6ee0c0cdb 100644 --- a/gdk/wayland/gdkprivate-wayland.h +++ b/gdk/wayland/gdkprivate-wayland.h @@ -93,7 +93,7 @@ void gdk_wayland_window_sync (GdkWindow *window); void _gdk_wayland_window_register_dnd (GdkWindow *window); GdkDragContext *_gdk_wayland_window_drag_begin (GdkWindow *window, GdkDevice *device, - GdkContentFormats *formats, + GdkContentProvider *content, GdkDragAction actions, gint dx, gint dy); diff --git a/gdk/wayland/gdkselection-wayland.c b/gdk/wayland/gdkselection-wayland.c index 4634033ab4..6d1904822a 100644 --- a/gdk/wayland/gdkselection-wayland.c +++ b/gdk/wayland/gdkselection-wayland.c @@ -753,63 +753,6 @@ gdk_wayland_selection_lookup_requestor_buffer (GdkWindow *requestor) return NULL; } -static gboolean -gdk_wayland_selection_source_handles_target (GdkWaylandSelection *wayland_selection, - GdkAtom target) -{ - GdkAtom atom; - guint i; - - if (target == NULL) - return FALSE; - - for (i = 0; i < wayland_selection->source_targets->len; i++) - { - atom = g_array_index (wayland_selection->source_targets, GdkAtom, i); - - if (atom == target) - return TRUE; - } - - return FALSE; -} - -static gboolean -gdk_wayland_selection_request_target (GdkWaylandSelection *wayland_selection, - GdkWindow *window, - GdkAtom selection, - GdkAtom target, - gint fd) -{ - if (wayland_selection->stored_selection.fd == fd && - wayland_selection->requested_target == target) - return FALSE; - - /* If we didn't issue gdk_wayland_selection_check_write() yet - * on a previous fd, it will still linger here. Just close it, - * as we can't have more than one fd on the fly. - */ - if (wayland_selection->stored_selection.fd >= 0) - close (wayland_selection->stored_selection.fd); - - wayland_selection->stored_selection.fd = fd; - wayland_selection->requested_target = target; - - if (window && - gdk_wayland_selection_source_handles_target (wayland_selection, target)) - { - gdk_wayland_selection_emit_request (window, selection, target); - return TRUE; - } - else - { - close (fd); - wayland_selection->stored_selection.fd = -1; - } - - return FALSE; -} - static void data_source_target (void *data, struct wl_data_source *source, @@ -821,44 +764,47 @@ data_source_target (void *data, } static void -data_source_send (void *data, - struct wl_data_source *source, - const char *mime_type, - int32_t fd) +gdk_wayland_drag_context_write_done (GObject *context, + GAsyncResult *result, + gpointer user_data) { - GdkWaylandSelection *wayland_selection = data; - GdkWindow *window; - GdkAtom selection; - - GDK_NOTE (EVENTS, - g_message ("data source send, source = %p, mime_type = %s, fd = %d", - source, mime_type, fd)); + GError *error = NULL; - if (!mime_type) + if (!gdk_drag_context_write_finish (GDK_DRAG_CONTEXT (context), result, &error)) { - close (fd); - return; + GDK_NOTE(DND, g_printerr ("%p: failed to write stream: %s\n", context, error->message)); + g_error_free (error); } +} - if (source == wayland_selection->dnd_source) - { - window = wayland_selection->dnd_owner; - selection = atoms[ATOM_DND]; - } - else - { - close (fd); - return; - } +static void +data_source_send (void *data, + struct wl_data_source *source, + const char *mime_type, + int32_t fd) +{ + GdkDragContext *context; + GOutputStream *stream; - if (!window) + context = gdk_wayland_drag_context_lookup_by_data_source (source); + if (!context) return; - if (!gdk_wayland_selection_request_target (wayland_selection, window, - selection, - gdk_atom_intern (mime_type, FALSE), - fd)) - gdk_wayland_selection_check_write (wayland_selection); + GDK_NOTE (DND, g_printerr ("%p: data source send request for %s on fd %d\n", + source, mime_type, fd)); + + //mime_type = gdk_intern_mime_type (mime_type); + mime_type = g_intern_string (mime_type); + stream = g_unix_output_stream_new (fd, TRUE); + + gdk_drag_context_write_async (context, + mime_type, + stream, + G_PRIORITY_DEFAULT, + NULL, + gdk_wayland_drag_context_write_done, + context); + g_object_unref (stream); } static void diff --git a/gdk/x11/gdkdnd-x11.c b/gdk/x11/gdkdnd-x11.c index 3443fb2a28..c36c10bf41 100644 --- a/gdk/x11/gdkdnd-x11.c +++ b/gdk/x11/gdkdnd-x11.c @@ -2857,25 +2857,25 @@ drag_context_ungrab (GdkDragContext *context) } GdkDragContext * -_gdk_x11_window_drag_begin (GdkWindow *window, - GdkDevice *device, - GdkContentFormats *formats, - GdkDragAction actions, - gint dx, - gint dy) +_gdk_x11_window_drag_begin (GdkWindow *window, + GdkDevice *device, + GdkContentProvider *content, + GdkDragAction actions, + gint dx, + gint dy) { GdkDragContext *context; int x_root, y_root; context = (GdkDragContext *) g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, "display", gdk_window_get_display (window), + "content", content, NULL); context->is_source = TRUE; context->source_window = window; g_object_ref (window); - context->formats = gdk_content_formats_ref (formats); precache_target_list (context); gdk_drag_context_set_device (context, device); diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h index 8d0919763e..6ebd0e8eb1 100644 --- a/gdk/x11/gdkprivate-x11.h +++ b/gdk/x11/gdkprivate-x11.h @@ -282,12 +282,12 @@ void _gdk_x11_cursor_display_finalize (GdkDisplay *display); void _gdk_x11_window_register_dnd (GdkWindow *window); -GdkDragContext * _gdk_x11_window_drag_begin (GdkWindow *window, - GdkDevice *device, - GdkContentFormats *formats, - GdkDragAction actions, - gint x_root, - gint y_root); +GdkDragContext * _gdk_x11_window_drag_begin (GdkWindow *window, + GdkDevice *device, + GdkContentProvider *content, + GdkDragAction actions, + gint dx, + gint dy); GdkGrabStatus _gdk_x11_convert_grab_status (gint status); diff --git a/gtk/gtkdnd.c b/gtk/gtkdnd.c index 085a7a9774..dd2a0034e0 100644 --- a/gtk/gtkdnd.c +++ b/gtk/gtkdnd.c @@ -960,6 +960,150 @@ gtk_drag_dest_drop (GtkWidget *widget, * Source side * ***************/ +#define GTK_TYPE_DRAG_CONTENT (gtk_drag_content_get_type ()) +#define GTK_DRAG_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_DRAG_CONTENT, GtkDragContent)) +#define GTK_IS_DRAG_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_DRAG_CONTENT)) +#define GTK_DRAG_CONTENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_DRAG_CONTENT, GtkDragContentClass)) +#define GTK_IS_DRAG_CONTENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_DRAG_CONTENT)) +#define GTK_DRAG_CONTENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_DRAG_CONTENT, GtkDragContentClass)) + +typedef struct _GtkDragContent GtkDragContent; +typedef struct _GtkDragContentClass GtkDragContentClass; + +struct _GtkDragContent +{ + GdkContentProvider parent; + + GtkWidget *widget; + GdkDragContext *context; + GdkContentFormats *formats; + guint32 time; +}; + +struct _GtkDragContentClass +{ + GdkContentProviderClass parent_class; +}; + +GType gtk_drag_content_get_type (void) G_GNUC_CONST; + +G_DEFINE_TYPE (GtkDragContent, gtk_drag_content, GDK_TYPE_CONTENT_PROVIDER) + +static GdkContentFormats * +gtk_drag_content_ref_formats (GdkContentProvider *provider) +{ + GtkDragContent *content = GTK_DRAG_CONTENT (provider); + + return gdk_content_formats_ref (content->formats); +} + +static void +gtk_drag_content_write_mime_type_done (GObject *stream, + GAsyncResult *result, + gpointer task) +{ + GError *error = NULL; + + if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), + result, + NULL, + &error)) + { + g_task_return_error (task, error); + } + else + { + g_task_return_boolean (task, TRUE); + } + + g_object_unref (task); +} + +static void +gtk_drag_content_write_mime_type_async (GdkContentProvider *provider, + const char *mime_type, + GOutputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GtkDragContent *content = GTK_DRAG_CONTENT (provider); + GtkSelectionData sdata = { 0, }; + GTask *task; + + task = g_task_new (content, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + g_task_set_source_tag (task, gtk_drag_content_write_mime_type_async); + + sdata.selection = gdk_drag_get_selection (content->context); + sdata.target = gdk_atom_intern (mime_type, FALSE); + sdata.length = -1; + sdata.display = gtk_widget_get_display (content->widget); + + g_signal_emit_by_name (content->widget, "drag-data-get", + content->context, + &sdata, + content->time); + + if (sdata.length == -1) + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Cannot provide contents as “%s”"), mime_type); + g_object_unref (task); + return; + } + g_task_set_task_data (task, sdata.data, g_free); + + g_output_stream_write_all_async (stream, + sdata.data, + sdata.length, + io_priority, + cancellable, + gtk_drag_content_write_mime_type_done, + task); +} + +static gboolean +gtk_drag_content_write_mime_type_finish (GdkContentProvider *provider, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, provider), FALSE); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gtk_drag_content_write_mime_type_async, FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +gtk_drag_content_finalize (GObject *object) +{ + GtkDragContent *content = GTK_DRAG_CONTENT (object); + + g_clear_object (&content->widget); + g_clear_pointer (&content->formats, (GDestroyNotify) gdk_content_formats_unref); + + G_OBJECT_CLASS (gtk_drag_content_parent_class)->finalize (object); +} + +static void +gtk_drag_content_class_init (GtkDragContentClass *class) +{ + GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class); + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->finalize = gtk_drag_content_finalize; + + provider_class->ref_formats = gtk_drag_content_ref_formats; + provider_class->write_mime_type_async = gtk_drag_content_write_mime_type_async; + provider_class->write_mime_type_finish = gtk_drag_content_write_mime_type_finish; +} + +static void +gtk_drag_content_init (GtkDragContent *content) +{ +} + /* Like gtk_drag_begin(), but also takes a GtkIconHelper * so that we can set the icon from the source site information */ @@ -979,6 +1123,7 @@ gtk_drag_begin_internal (GtkWidget *widget, GdkWindow *ipc_window; int dx, dy; GdkAtom selection; + GtkDragContent *content; guint32 time; ipc_widget = gtk_drag_get_ipc_widget (widget); @@ -1001,13 +1146,22 @@ gtk_drag_begin_internal (GtkWidget *widget, dx -= x; dy -= y; - context = gdk_drag_begin (ipc_window, device, target_list, actions, dx, dy); + content = g_object_new (GTK_TYPE_DRAG_CONTENT, NULL); + content->widget = g_object_ref (widget); + content->formats = gdk_content_formats_ref (target_list); + content->time = time; + + context = gdk_drag_begin (ipc_window, device, GDK_CONTENT_PROVIDER (content), actions, dx, dy); if (context == NULL) { gtk_drag_release_ipc_widget (ipc_widget); + g_object_unref (content); return NULL; } + content->context = context; + g_object_unref (content); + info = gtk_drag_get_source_info (context, TRUE); info->ipc_widget = ipc_widget; -- 2.30.2